From 2604adda4f817c3d9bf81a9c45b2bafda79ec4e9 Mon Sep 17 00:00:00 2001 From: Keir Fraser Date: Fri, 2 Jul 2010 19:04:57 +0100 Subject: [PATCH] AMD OSVW (OS Visible Workaround) for Xen This path enables AMD OSVW (OS Visible Workaround) feature for Xen. New AMD errata will have a OSVW id assigned in the future. OS is supposed to check OSVW status MSR to find out whether CPU has a specific erratum. Legacy errata are also supported in this patch: traditional family/model/stepping approach will be used if OSVW feature isn't applicable. This patch is adapted from Hans Rosenfeld's patch submitted to Linux kernel. Signed-off-by: Wei Huang Signed-off-by: Hans Rosenfeld Acked-by: Jan Beulich --- xen/arch/x86/cpu/amd.c | 50 ++++++++++++++++++++- xen/arch/x86/hvm/svm/asid.c | 4 +- xen/arch/x86/hvm/svm/svm.c | 5 ++- xen/{arch/x86/cpu => include/asm-x86}/amd.h | 35 +++++++++++++++ xen/include/asm-x86/msr-index.h | 4 ++ 5 files changed, 93 insertions(+), 5 deletions(-) rename xen/{arch/x86/cpu => include/asm-x86}/amd.h (72%) diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c index ec378a2a8c..6cfd337378 100644 --- a/xen/arch/x86/cpu/amd.c +++ b/xen/arch/x86/cpu/amd.c @@ -7,12 +7,12 @@ #include #include #include +#include #include #include /* amd_init_cpu */ #include #include "cpu.h" -#include "amd.h" /* * Pre-canned values for overriding the CPUID features @@ -147,6 +147,54 @@ static void __devinit set_cpuidmask(const struct cpuinfo_x86 *c) } } +/* + * Check for the presence of an AMD erratum. Arguments are defined in amd.h + * for each known erratum. Return 1 if erratum is found. + */ +int cpu_has_amd_erratum(const struct cpuinfo_x86 *cpu, int osvw, ...) +{ + va_list ap; + u32 range; + u32 ms; + + if (cpu->x86_vendor != X86_VENDOR_AMD) + return 0; + + va_start(ap, osvw); + + if (osvw) { + u16 osvw_id = va_arg(ap, int); + + if (cpu_has(cpu, X86_FEATURE_OSVW)) { + u64 osvw_len; + rdmsrl(MSR_AMD_OSVW_ID_LENGTH, osvw_len); + + if (osvw_id < osvw_len) { + u64 osvw_bits; + rdmsrl(MSR_AMD_OSVW_STATUS + (osvw_id >> 6), + osvw_bits); + + va_end(ap); + return (osvw_bits >> (osvw_id & 0x3f)) & 0x01; + } + } + } + + /* OSVW unavailable or ID unknown, match family-model-stepping range */ + ms = (cpu->x86_model << 8) | cpu->x86_mask; + while ((range = va_arg(ap, int))) { + if ((cpu->x86 == AMD_MODEL_RANGE_FAMILY(range)) && + (ms >= AMD_MODEL_RANGE_START(range)) && + (ms <= AMD_MODEL_RANGE_END(range))) { + va_end(ap); + return 1; + } + } + + va_end(ap); + return 0; +} + /* * amd_flush_filter={on,off}. Forcibly Enable or disable the TLB flush * filter on AMD 64-bit processors. diff --git a/xen/arch/x86/hvm/svm/asid.c b/xen/arch/x86/hvm/svm/asid.c index 1deb2e8b4a..3a35e150ed 100644 --- a/xen/arch/x86/hvm/svm/asid.c +++ b/xen/arch/x86/hvm/svm/asid.c @@ -21,14 +21,14 @@ #include #include #include +#include void svm_asid_init(struct cpuinfo_x86 *c) { int nasids = 0; /* Check for erratum #170, and leave ASIDs disabled if it's present. */ - if ( (c->x86 == 0x10) || - ((c->x86 == 0xf) && (c->x86_model >= 0x68) && (c->x86_mask >= 1)) ) + if ( !cpu_has_amd_erratum(c, AMD_ERRATUM_170) ) nasids = cpuid_ebx(0x8000000A); hvm_asid_init(nasids); diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c index 0953719fe8..c6455b739e 100644 --- a/xen/arch/x86/hvm/svm/svm.c +++ b/xen/arch/x86/hvm/svm/svm.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -846,8 +847,8 @@ static void svm_init_erratum_383(struct cpuinfo_x86 *c) { uint64_t msr_content; - /* only family 10h is affected */ - if ( c->x86 != 0x10 ) + /* check whether CPU is affected */ + if ( !cpu_has_amd_erratum(c, AMD_ERRATUM_383) ) return; /* use safe methods to be compatible with nested virtualization */ diff --git a/xen/arch/x86/cpu/amd.h b/xen/include/asm-x86/amd.h similarity index 72% rename from xen/arch/x86/cpu/amd.h rename to xen/include/asm-x86/amd.h index 4da3d64a87..c38984f553 100644 --- a/xen/arch/x86/cpu/amd.h +++ b/xen/include/asm-x86/amd.h @@ -100,4 +100,39 @@ __bit(X86_FEATURE_SKINIT)) #define AMD_EXTFEATURES_FAM11h_REV_B_EDX AMD_EXTFEATURES_K8_REV_G_EDX +/* AMD errata checking + * + * Errata are defined using the AMD_LEGACY_ERRATUM() or AMD_OSVW_ERRATUM() + * macros. The latter is intended for newer errata that have an OSVW id + * assigned, which it takes as first argument. Both take a variable number + * of family-specific model-stepping ranges created by AMD_MODEL_RANGE(). + * + * Example 1: + * #define AMD_ERRATUM_319 \ + * AMD_LEGACY_ERRATUM(AMD_MODEL_RANGE(0x10, 0x2, 0x1, 0x4, 0x2), \ + * AMD_MODEL_RANGE(0x10, 0x8, 0x0, 0x8, 0x0), \ + * AMD_MODEL_RANGE(0x10, 0x9, 0x0, 0x9, 0x0)) + * Example 2: + * #define AMD_ERRATUM_400 \ + * AMD_OSVW_ERRATUM(1, AMD_MODEL_RANGE(0xf, 0x41, 0x2, 0xff, 0xf), \ + * AMD_MODEL_RANGE(0x10, 0x2, 0x1, 0xff, 0xf)) + * + */ + +#define AMD_LEGACY_ERRATUM(...) 0 /* legacy */, __VA_ARGS__, 0 +#define AMD_OSVW_ERRATUM(osvw_id, ...) 1 /* osvw */, osvw_id, __VA_ARGS__, 0 +#define AMD_MODEL_RANGE(f, m_start, s_start, m_end, s_end) \ + ((f << 24) | (m_start << 16) | (s_start << 12) | (m_end << 4) | (s_end)) +#define AMD_MODEL_RANGE_FAMILY(range) (((range) >> 24) & 0xff) +#define AMD_MODEL_RANGE_START(range) (((range) >> 12) & 0xfff) +#define AMD_MODEL_RANGE_END(range) ((range) & 0xfff) + +#define AMD_ERRATUM_170 \ + AMD_LEGACY_ERRATUM(AMD_MODEL_RANGE(0x0f, 0x0, 0x0, 0x67, 0xf)) + +#define AMD_ERRATUM_383 \ + AMD_OSVW_ERRATUM(3, AMD_MODEL_RANGE(0x10, 0x2, 0x1, 0xff, 0xf), \ + AMD_MODEL_RANGE(0x12, 0x0, 0x0, 0x1, 0x0)) + +int cpu_has_amd_erratum(const struct cpuinfo_x86 *, int, ...); #endif /* __AMD_H__ */ diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h index 1db710156c..150e19154f 100644 --- a/xen/include/asm-x86/msr-index.h +++ b/xen/include/asm-x86/msr-index.h @@ -252,6 +252,10 @@ #define MSR_AMD_PATCHLEVEL 0x0000008b #define MSR_AMD_PATCHLOADER 0xc0010020 +/* AMD OS Visible Workaround MSRs */ +#define MSR_AMD_OSVW_ID_LENGTH 0xc0010140 +#define MSR_AMD_OSVW_STATUS 0xc0010141 + /* K6 MSRs */ #define MSR_K6_EFER 0xc0000080 #define MSR_K6_STAR 0xc0000081 -- 2.30.2